package com.yjzx.util;

import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Construct;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.nodes.*;
import org.yaml.snakeyaml.representer.Representer;

import java.io.*;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author guozhi
 * @date 2024/4/19
 * @description YamlBeanUpperUtil
 */
public enum YamlToBean {
    /**
     * 默认的
     */
    normal,
    /**
     * 首字母大写的
     */
    titleCase {
        @Override
        public <T> T yamlToJavaBean(Reader io, Class<T> clazz) {
            return yamlToUpperBean.loadAs(io, clazz);
        }

        @Override
        public <T> T yamlToJavaBean(InputStream input, Class<T> clazz) {
            return yamlToUpperBean.loadAs(input, clazz);
        }

        @Override
        public <T> T yamlToJavaBean(String yaml, Class<T> clazz) {
            return yamlToUpperBean.loadAs(yaml, clazz);
        }
    },
    /**
     * 自动转换
     */
    auto {
        @Override
        public <T> T yamlToJavaBean(Reader io, Class<T> clazz) {
            char[] buffer, array;
            try {
                buffer = new char[1024];
                array = new char[0];
                int len;
                int bytesRead;
                do {
                    bytesRead = io.read(buffer);
                    len = bytesRead != -1 ? bytesRead : 0;
                    char[] arrayNew = Arrays.copyOf(array, array.length + len);
                    System.arraycopy(buffer, 0, arrayNew, array.length, len);
                    array = arrayNew;
                }
                while (bytesRead != -1);
            } catch (IOException e) {
                e.printStackTrace();
                throw new RuntimeException("读取不到数据");
            }
            T t;
            try {
                t = yamlToBean.loadAs(new CharArrayReader(array), clazz);
            } catch (YAMLException e) {
                t = yamlToUpperBean.loadAs(new CharArrayReader(array), clazz);
            }
            return t;
        }

        @Override
        public <T> T yamlToJavaBean(InputStream input, Class<T> clazz) {
            byte[] buffer, array;
            try {
                buffer = new byte[1024];
                array = new byte[0];
                int len;
                int bytesRead;
                do {
                    bytesRead = input.read(buffer);
                    len = bytesRead != -1 ? bytesRead : 0;
                    byte[] arrayNew = Arrays.copyOf(array, array.length + len);
                    System.arraycopy(buffer, 0, arrayNew, array.length, len);
                    array = arrayNew;
                }
                while (bytesRead != -1);
            } catch (IOException e) {
                e.printStackTrace();
                throw new RuntimeException("读取不到数据");
            }

            T t;
            try {
                t = yamlToBean.loadAs(new ByteArrayInputStream(array), clazz);
            } catch (YAMLException e) {
                t = yamlToUpperBean.loadAs(new ByteArrayInputStream(array), clazz);
            }
            return t;
        }

        @Override
        public <T> T yamlToJavaBean(String yaml, Class<T> clazz) {
            T t;
            try {
                t = yamlToBean.loadAs(yaml, clazz);
            } catch (YAMLException e) {
                t = yamlToUpperBean.loadAs(yaml, clazz);
            }
            return t;
        }
    };

    static final Yaml yamlToBean = new Yaml();
    static final Yaml yamlToUpperBean = new Yaml(new Constructor(new LoaderOptions()) {
        public NodeTuple lowercaseToUppercase(NodeTuple tuple) {

            return new NodeTuple(lowercaseToUppercase(tuple.getKeyNode()), lowercaseToUppercase(tuple.getValueNode()));
        }

        public <T extends Node> T lowercaseToUppercase(T node) {
            if (node instanceof MappingNode) {
                List<NodeTuple> value = ((MappingNode) node).getValue().stream().map(this::lowercaseToUppercase)
                        .collect(Collectors.toList());
                return (T) new MappingNode(node.getTag(), value, ((MappingNode) node).getFlowStyle());
            } else if (node instanceof ScalarNode) {
                ScalarNode scalarNode = (ScalarNode) node;
                String value = scalarNode.getValue();
                if (StringUtils.isNotBlank(value)) {
                    StringBuilder builder = new StringBuilder();
                    boolean flag = false;
                    for (int i = 0; i < value.length(); i++) {
                        if ("-".equals("" + value.charAt(i))) {
                            flag = true;
                        } else {
                            if (flag) {
                                builder.append(Character.toUpperCase(value.charAt(i)));
                                flag = false;
                            } else {
                                builder.append(value.charAt(i));
                            }
                        }
                    }
                    value = builder.toString();
                }
                return (T) new ScalarNode(scalarNode.getTag(), value, scalarNode.getStartMark(), scalarNode.getEndMark(), scalarNode.getScalarStyle());
            } else if (node instanceof SequenceNode) {
                SequenceNode sequenceNode = (SequenceNode) node;
                List<Node> collect = sequenceNode.getValue().stream().map(x -> lowercaseToUppercase(x)).collect(Collectors.toList());
                return (T) new SequenceNode(sequenceNode.getTag(), collect, sequenceNode.getFlowStyle());
            }
            throw new RuntimeException("未考虑到的Node类型:" + node.getClass().toString());
        }

        @Override
        public Object getSingleData(Class<?> type) {
            Node node = lowercaseToUppercase(this.composer.getSingleNode());
            if (node != null && !Tag.NULL.equals(node.getTag())) {
                if (Object.class != type) {
                    node.setTag(new Tag(type));
                } else if (this.rootTag != null) {
                    node.setTag(this.rootTag);
                }

                return this.constructDocument(node);
            } else {
                Construct construct = this.yamlConstructors.get(Tag.NULL);
                return construct.construct(node);
            }
        }
    }, new Representer(new DumperOptions()));

    public <T> T yamlToJavaBean(Reader io, Class<T> clazz) {
        return yamlToBean.loadAs(io, clazz);
    }

    ;

    public <T> T yamlToJavaBean(InputStream input, Class<T> clazz) {
        return yamlToBean.loadAs(input, clazz);
    }

    public <T> T yamlToJavaBean(String yaml, Class<T> clazz) {
        return yamlToBean.loadAs(yaml, clazz);
    }
}
